
// <copyright file="Generic_Pool_Tests_With_Non_Shared_Objects.cs" company="Subhadra Software Solutions">
//   
// </copyright>
// <summary>
//   The generic_ pool_ tests_ with_ non_ shared_ objects.
// </summary>

namespace SubhadraSolutions.Sharp.Utils.Tests.Pooling
{
    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;

    using Microsoft.VisualStudio.TestTools.UnitTesting;

    using SubhadraSolutions.Sharp.Utils.Pooling;

    /// <summary>
    /// The generic_ pool_ tests_ with_ non_ shared_ objects.
    /// </summary>
    [TestClass]
    public class Generic_Pool_Tests_With_Non_Shared_Objects
    {
        #region Public Methods and Operators

        /// <summary>
        /// The pool asynchronous test.
        /// </summary>
        [TestMethod]
        public void PoolAsynchronousTest()
        {
            const int maxPoolSize = 1000;
            const int objectMaxIdleTimeInMilliseconds = 5000;

            var pool = new GenericPool<DummyDisposableObject> { CanObjectBeShared = false, MaxPoolSize = maxPoolSize, ObjectMaxIdleTimeInMilliseconds = objectMaxIdleTimeInMilliseconds };
            var bag = new ConcurrentBag<GenericPoolItem<DummyDisposableObject>>();
            var e = new CountdownEvent(10);
            for (int i = 0; i < maxPoolSize; i += 100)
            {
                Task.Factory.StartNew(state =>
                    {
                        var kvp = (KeyValuePair<int, int>)state;
                        for (int j = kvp.Key; j < kvp.Value; j++)
                        {
                            GenericPoolItem<DummyDisposableObject> poolItem = pool.GetObject();
                            poolItem.AdaptedObject.Id = j;
                            bag.Add(poolItem);
                        }

                        e.Signal();
                    }, new KeyValuePair<int, int>(i, i + 100));
            }

            e.Wait();
            Assert.IsTrue(pool.Count == maxPoolSize);

            // Lets try to get an object from pool when the pool is full and has no free objects.
            // Ideally we should not get any object
            GenericPoolItem<DummyDisposableObject> temp = null;
            bool hasGotObject = pool.TryGetObject(out temp, 2000);
            Assert.IsTrue(!hasGotObject);

            // Now, lets try to Dispose one object by using another thread while this thread is waiting for an object
            // Unlike the earlier case, we should get an object this time
            GenericPoolItem<DummyDisposableObject> temp1 = null;
            bag.TryPeek(out temp1);
            DummyDisposableObject adaptedObject = temp1.AdaptedObject;
            var evt = new ManualResetEvent(false);
            Task.Factory.StartNew(delegate
                {
                    evt.Set();
                    Thread.Sleep(2000);
                    temp1.Dispose();
                });
            evt.WaitOne();
            hasGotObject = pool.TryGetObject(out temp, 5000);
            Assert.IsTrue(hasGotObject);
            Assert.IsTrue(adaptedObject.Id == temp.AdaptedObject.Id);
        }

        /// <summary>
        /// The pool synchronous test.
        /// </summary>
        [TestMethod]
        public void PoolSynchronousTest()
        {
            const int maxPoolSize = 10;
            const int objectMaxIdleTimeInMilliseconds = 5000;

            var pool = new GenericPool<DummyDisposableObject> { CanObjectBeShared = false, MaxPoolSize = maxPoolSize, ObjectMaxIdleTimeInMilliseconds = objectMaxIdleTimeInMilliseconds };
            var list = new List<GenericPoolItem<DummyDisposableObject>>();
            for (int i = 0; i < maxPoolSize; i++)
            {
                GenericPoolItem<DummyDisposableObject> poolItem = pool.GetObject();
                poolItem.AdaptedObject.Id = i;
                list.Add(poolItem);
            }

            Assert.IsTrue(pool.Count == maxPoolSize);

            // Lets try to get an object from pool when the pool is full and has no free objects.
            // Ideally we should not get any object
            GenericPoolItem<DummyDisposableObject> temp = null;
            bool hasGotObject = pool.TryGetObject(out temp, 2000);
            Assert.IsTrue(!hasGotObject);

            // Lets Dispose one object so that there will be one free object in the pool.
            // and then lets try to get an object from the pool. This time pool should return an object.
            DummyDisposableObject adaptedObject = list[0].AdaptedObject;
            list[0].Dispose();
            Assert.IsTrue(list[0].IsDisposed);
            try
            {
                // Accessing the AdaptedObjectProtected property should throw ObjectDisposedException
                object o = list[0].AdaptedObject;
                Assert.Fail("If you are here, it means it is failed." + o);
            }
            catch (ObjectDisposedException)
            {
            }

            // check that the object in the pool is not disposed immediately. Because, it has to wait for ObjectMaxIdleTimeInMilliseconds duration
            Assert.IsTrue(!adaptedObject.IsDisposed);
            hasGotObject = pool.TryGetObject(out temp, 2000);
            Assert.IsTrue(hasGotObject);

            // Check if the same object is recycled.
            Assert.IsTrue(temp.AdaptedObject.Id == 0);
            Assert.IsTrue(pool.Count == maxPoolSize);

            adaptedObject = temp.AdaptedObject;
            Assert.IsFalse(adaptedObject.IsDisposed);
            temp.Dispose();
            Assert.IsTrue(temp.IsDisposed);
            try
            {
                // Accessing the AdaptedObjectProtected property should throw ObjectDisposedException
                object o = temp.AdaptedObject;
                Assert.Fail("If you are here, it means it is failed." + o);
            }
            catch (ObjectDisposedException)
            {
            }

            // After this sleep, the object from the pool should have been disposed.
            Thread.Sleep(objectMaxIdleTimeInMilliseconds + 100);
            Assert.IsTrue(adaptedObject.IsDisposed);
            Assert.IsTrue(pool.Count == maxPoolSize - 1);
        }

        #endregion Public Methods and Operators
    }
}
